[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.Addition.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.Addition.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Button.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Button.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.ExtenderDuplicator.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.ExtenderDuplicator.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.FieldConstraints.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.FieldConstraints.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Iframe.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Iframe.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.NodeBuilder.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.NodeBuilder.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Path.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Path.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Stylesheet.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Stylesheet.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Root.js", "application/x-javascript")]
[assembly: System.Web.UI.ScriptResource("BM.Tools.WebControls.MForm.Controls.Root.js", "BM.Tools.WebControls.MForm.Resources.MessageResource", "BM.Messages")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Root.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.CulturalContext.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.CulturalContext.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.MBasis.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.MBasis.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Validators.ValidationError.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Validators.ValidationError.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Resources.js.jquery.jquery.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Resources.js.jquery.jquery.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Navigation.ItemsNavigator.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Navigation.ItemsNavigator.debug.js", "application/x-javascript")]

namespace BM.Tools.WebControls.MForm.Controls
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Xml;
    using System.Xml.XPath;

    using AjaxControlToolkit;
    using BM.Tools.WebControls.MForm.Controls.Additions;
    using BM.Tools.WebControls.MForm.Controls.Helper;
    using BM.Tools.WebControls.MForm.Controls.Helper.EventValidation;
    using BM.Tools.WebControls.MForm.Controls.Items;
    using BM.Tools.WebControls.MForm.Design;
    using BM.Tools.WebControls.MForm.Generator;
    using BM.Tools.WebControls.MForm.Navigation;

    /// <summary>
    /// A delegate of the method which will create the offspring's display name.
    /// </summary>
    /// <param name="sender">
    /// The sender.
    /// </param>
    /// <param name="args">
    /// The arguments.
    /// </param>
    /// <returns>
    /// the display name text.
    /// </returns>        
    public delegate string CreateDisplayName(Offspring sender, EventArgs args);

    /// <summary>
    /// The root control of the MForm control tree
    /// </summary>    
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Button.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.MBasis.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.NodeBuilder.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Path.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Root.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.CulturalContext.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Validators.ValidationError.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.ExtenderDuplicator.js")]    
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Helper.RemovalButton.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Navigation.ItemsNavigator.js")]
    [ToolboxData(@"<{0}:Root runat=""server"" ID="" ""  ></{0}:Root>")]
    [Designer(typeof(RootDesigner))]
    [ValidationProperty("Root")]
    public class Root : OffspringsOwner
    {        
        public const string DefaultPrefix = "_def_";                

        private CulturalContext culturalContext;

        private CreateDisplayName displayNameMethod = DisplayNameHelper.DefaultDisplayName;

        private FormBuilderMode mode = FormBuilderMode.Edit;

        private RootScriptHelper rootScriptHelper;                

        private PlaceHolder hiddenInputs = new PlaceHolder();

        /// <summary>
        /// Initializes a new instance of the <see cref="Root"/> class. 
        /// </summary>
        public Root()
        {
            ShouldLoadJQuery = true;
            ShouldAutomaticallyAddDataTypeConstraints = true;
            ContainerCssClass = "MForm-Container";            
            UriMappings = new UriMappings();
        }

        /// <summary>
        /// Gets or sets the css class of the container div.
        /// </summary>
        [DefaultValue("MFormContainer")]
        public string ContainerCssClass
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the object that keeps MForm cultural settings.
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]        
        public CulturalContext CulturalContext
        {
            get
            {
                if (IsDuringGeneration)
                {
                    return null;
                }

                if (culturalContext == null)
                {
                    culturalContext = new CulturalContext();
                }

                return culturalContext;
            }            
        }

        /// <summary>
        /// Gets or sets the method of how to create the default display name method.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public CreateDisplayName DisplayNameMethod
        {
            get
            {
                return displayNameMethod;
            }

            set
            {
                displayNameMethod = value;
            }
        }

        /// <summary>
        /// Gets the document element item.
        /// </summary>
        public OffspringItem DocumentElementItem
        {
            get
            {
                if (Items.Count == 0)
                {
                    return null;
                }

                var rootItem = Items[0] as RootItem;
                return rootItem.ChildItems.Where(item => !item.IsPrototypeItem).FirstOrDefault();
            }
        } 

        /// <summary>
        /// Gets or sets the form builder mode.
        /// </summary>        
        [DefaultValue(FormBuilderMode.Edit)]
        public override FormBuilderMode Mode
        {
            get
            {
                return mode;
            }

            set
            {
                mode = value;
            }
        }

        /// <summary>
        /// Gets or sets the client-side event handler name that is fired when the whole MForm load is complete.
        /// </summary>
        public string OnClientLoadComplete
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the client-side event handler name that is fired everytime an item on the client-side is created. 
        /// It is important to know that the items generated on the server side are also 'created' during the ASP.NET Ajax loading phase.
        /// The first parameter in the handler method is the Root client-side control, the args contain an item field with the newly created client-side item control.
        /// </summary>
        public string OnClientItemCreated
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the client-side event handler name that is fired everytime an item on the client-side is removed.         
        /// The first parameter in the handler method is the Root client-side control, the args contain an item field with to-be-removed client-side item control.
        /// </summary>
        public string OnClientItemRemoved
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the output xml.
        /// </summary>        
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode", Justification = "Specific functionality provided by the concrete type is required")]
        public XmlDocument OutputXml
        {
            get
            {
                if (DocumentElementItem == null)
                {
                    return null;
                }

                return DocumentElementItem.GetOutputXml();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the root should load jQuery javascript library. 
        /// If not, this library must be added to the page by hand
        /// </summary>
        [DefaultValue(true)]
        public bool ShouldLoadJQuery
        {
            get; set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether all the leaf items inside the root should automatically add <see cref="DataTypeConstraint"/> constraint.
        /// </summary>
        [DefaultValue(true)]
        public bool ShouldAutomaticallyAddDataTypeConstraints
        {
            get; 
            set;
        }

        /// <summary>
        /// Gets ParentBasis.
        /// </summary>
        public override OffspringsOwner ParentBasis
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether items in view mode should be validated.
        /// </summary>
        [DefaultValue(false)]
        public bool ShouldValidateInViewMode
        {
            get;
            set;
        }

        internal PlaceHolder HiddenInputs 
        {
            get
            {
                return hiddenInputs;
            }
        }       

        /// <summary>
        /// Gets XPathString.
        /// </summary>
        /// <value>
        /// The x path string.
        /// </value>
        internal override string XPathString
        {
            get
            {
                return ".";
            }
        }

        /// <summary>
        /// Gets RootScriptHelper.
        /// </summary>
        /// <value>
        /// The root script helper.
        /// </value>
        protected RootScriptHelper RootScriptHelper
        {
            get
            {
                if (rootScriptHelper == null)
                {
                    rootScriptHelper = new RootScriptHelper(this);
                }

                return rootScriptHelper;
            }
        }

        /// <summary>
        /// Renders the div tag, the client id attribute and a ContainerCssClass css class.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"{0}\" class=\"{1}\">", ClientID, ContainerCssClass);
        }

        /// <summary>
        /// Ends the div tag.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        public override void RenderEndTag(HtmlTextWriter writer)
        {
            writer.Write("</div>");
        }

        /// <summary>
        /// Selects offspring item ancestors, matching the path query
        /// </summary>
        /// <param name="path">
        /// The xpath-like path.
        /// </param>
        /// <returns>
        /// the collection of <see cref="OffspringItem" /> objects.
        /// </returns>
        public IEnumerable<OffspringItem> Select(string path)
        {
            if (Items.Count == 0)
            {
                return null;
            }

            var navigator = new OffspringsNavigator(Items[0], path);
            return navigator.Select();
        }

        /// <summary>
        /// Selects the first matching offspring item ancestor, matching the path query
        /// </summary>
        /// <param name="path">
        /// The xpath-like path.
        /// </param>
        /// <returns>
        /// the <see cref="OffspringItem" /> object or null.
        /// </returns>
        public OffspringItem SelectSingle(string path)
        {
            if (Items.Count == 0)
            {
                return null;
            }

            var navigator = new OffspringsNavigator(Items[0], path);
            return navigator.SelectSingle();
        }

        /// <summary>
        /// gets script descriptors
        /// </summary>
        /// <returns>
        /// the script descriptors
        /// </returns>
        public override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            yield return RootScriptHelper.GetRootDescriptor();
            foreach (var descriptor in base.GetScriptDescriptors())
            {
                yield return descriptor;
            }                   
        }

        /// <summary>
        /// Returns all the script references and jquery if <see cref="ShouldLoadJQuery"/> is set to true
        /// </summary>
        /// <returns>
        /// script references
        /// </returns>
        public override IEnumerable<ScriptReference> GetScriptReferences()
        {
            foreach (var scriptReference in base.GetScriptReferences())
            {
                yield return scriptReference;
            }
            
            if (ShouldLoadJQuery)
            {
                yield return new ScriptReference("BM.Tools.WebControls.MForm.Resources.js.jquery.jquery.js", "BM.Tools.WebControls.MForm");
            }
        }

        /// <summary>
        /// Adds the item and ensures that the created root item will be provided with the proepr data source.
        /// </summary>
        /// <param name="nav">
        /// the navigator that becomes a data source of the created item
        /// </param>
        /// <param name="index">
        /// the index of the item
        /// </param>        
        /// <returns>
        /// the generated item
        /// </returns>
        protected override OffspringItem AddItem(XPathNavigator nav, int index)
        {
            var item = CreateItem(index) as RootItem;
            OnItemCreated(new OffspringItemEventArgs(item, index));
            item.DataSource = this.GetDataSource();
            Controls.Add(item);
            items.Add(item);
            PostBackHighestIndex = HighestIndex;
            return item;
        }

        /// <summary>
        /// Creates child controls 
        /// </summary>
        /// <param name="dataBind">
        /// Should perform a databinding on children
        /// </param>
        protected override void CreateChildControls(bool dataBind)
        {
            ResetChildControls();
            ChildControlsCreated = true;
            AddItem(null);
        }

        /// <summary>        
        /// Creates an <see cref="RootItem" /> object from this object's definitions.
        /// </summary>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <returns>
        /// the <see cref="RootItem"/> object
        /// </returns>
        protected override OffspringItem CreateItem(int index)
        {
            return new RootItem(this, index);
        }

        /// <summary>
        /// Registers associated embedded resources.
        /// </summary>
        /// <param name="e">
        /// The event args.
        /// </param>
        protected override void OnPreRender(EventArgs e)
        {
            RootScriptHelper.RegisterScripts();                        
            base.OnPreRender(e);            
        }

        /// <summary>
        /// Renders the control.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        protected override void Render(HtmlTextWriter writer)
        {
            var eventValidationHelper = new EventValidationHelper(this);
            eventValidationHelper.Register();
            base.Render(writer);
        }

        /// <summary>
        /// Renders the children controls.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        protected override void RenderChildren(HtmlTextWriter writer)
        {
            if (DesignMode)
            {
                EnsureChildControls();
            }

            HiddenInputs.RenderControl(writer);            

            if (Items.Count > 0)
            {
                foreach (Control c in Items)
                {
                    c.RenderControl(writer);
                }
            }
        }        
    }
}